---
title: "07-Object 类与 equals-hashCode"
aliases:
- "Object 类与 equals-hashCode"
created: 2025-12-25
---
# Object 类与 equals/hashCode
## **一、Object 类概述**
### **1.1 Object 类的地位**
```mermaid
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#e3f2fd",
"primaryTextColor": "#0d47a1",
"primaryBorderColor": "#2196f3",
"lineColor": "#546e7a",
"fontSize": "14px",
"tertiaryColor": "#fdfdfe"
},
"flowchart": { "curve": "basis", "htmlLabels": true }
}}%%
flowchart TB
%% 样式定义
classDef root fill:#e8f5e9,stroke:#4caf50,stroke-width:2.5px,color:#1b5e20;
classDef mid fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
classDef leaf fill:#f3e5f5,stroke:#9c27b0,stroke-width:1.5px,color:#4a148c;
classDef info fill:#fff3e0,stroke:#ff9800,stroke-dasharray: 5 5,color:#e65100;
%% 核心继承结构
subgraph Hierarchy ["Java 类继承体系"]
direction TB
Root["Object
(所有类的根类)"]
subgraph Level1 ["第一层继承"]
StringNode["String"]
NumberNode["Number"]
CustomClass["你的自定义类"]
end
subgraph Level2 ["第二层继承 (以 Number 为例)"]
IntegerNode["Integer"]
DoubleNode["Double"]
LongNode["Long"]
end
%% 建立连接
Root ==> StringNode
Root ==> NumberNode
Root ==> CustomClass
NumberNode --> IntegerNode
NumberNode --> DoubleNode
NumberNode --> LongNode
end
%% 特点说明
subgraph Features ["Object 类核心特点"]
F1["所有类直接或间接继承 Object"]
F2["省略 extends 时编译器自动补全"]
F3["所有对象均拥有 Object 的基础方法"]
end
%% 节点应用样式
class Root root;
class StringNode,NumberNode,CustomClass mid;
class IntegerNode,DoubleNode,LongNode leaf;
class F1,F2,F3 info;
%% 布局辅助
Hierarchy ~~~ Features
```
```java
// 这两种写法等价
public class Person { }
public class Person extends Object { }
```
### **1.2 Object 类的 11 个方法**
```mermaid
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#e3f2fd",
"primaryTextColor": "#0d47a1",
"primaryBorderColor": "#2196f3",
"lineColor": "#546e7a",
"fontSize": "14px",
"tertiaryColor": "#f5f5f5"
},
"flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%
flowchart LR
%% 核心根节点
Root(["Java Object 类方法全景图"])
%% 分支 1:比较与标识
subgraph Group1 ["1. 对象比较与标识"]
direction TB
M1_1["equals(Object obj)
对象相等性比较"]
M1_2["hashCode()
返回对象哈希码"]
M1_3["toString()
返回对象字符串表示"]
end
%% 分支 2:对象克隆
subgraph Group2 ["2. 对象克隆"]
M2_1["clone()
创建并返回对象副本"]
end
%% 分支 3:类型信息
subgraph Group3 ["3. 类型信息"]
M3_1["getClass()
获取运行时 Class 对象"]
end
%% 分支 4:线程同步
subgraph Group4 ["4. 线程同步 (Monitor)"]
direction TB
M4_1["wait() / wait(ms) / wait(ms, ns)
线程进入等待状态"]
M4_2["notify()
唤醒单个等待线程"]
M4_3["notifyAll()
唤醒所有等待线程"]
end
%% 分支 5:垃圾回收
subgraph Group5 ["5. 垃圾回收"]
M5_1["finalize()
对象销毁前回调 (已废弃)"]
end
%% 连接关系
Root ==> Group1
Root ==> Group2
Root ==> Group3
Root ==> Group4
Root ==> Group5
%% 样式定义
classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
classDef term fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
classDef storage fill:#f3e5f5,stroke:#9c27b0,stroke-width:1.5px,color:#4a148c;
classDef deprecated fill:#ffebee,stroke:#f44336,stroke-width:1px,color:#b71c1c;
%% 应用样式
class Root main;
class M1_1,M1_2,M1_3 main;
class M2_1 storage;
class M3_1 term;
class M4_1,M4_2,M4_3 decision;
class M5_1 deprecated;
```
### **1.3 方法详情表**
| **方法** | **修饰符** | **返回类型** | **说明** |
| --- | --- | --- | --- |
| `getClass()` | public final native | Class> | 获取运行时类对象 |
| `hashCode()` | public native | int | 返回对象哈希码 |
| `equals(Object obj)` | public | boolean | 判断对象是否相等 |
| `clone()` | protected native | Object | 创建并返回对象副本 |
| `toString()` | public | String | 返回对象字符串表示 |
| `notify()` | public final native | void | 唤醒一个等待线程 |
| `notifyAll()` | public final native | void | 唤醒所有等待线程 |
| `wait()` | public final | void | 使当前线程等待 |
| `wait(long)` | public final native | void | 限时等待 |
| `wait(long, int)` | public final | void | 精确限时等待 |
| `finalize()` | protected | void | GC 回收前调用(已废弃) |
> ***native**:表示方法由 C/C++ 实现,不是 Java 代码*
> ***final**:表示方法不能被重写*
### **1.4 核心方法源码**
```java
/**
* Object 类核心方法源码(简化版)
*/
public class Object {
// ===== 1. getClass() =====
// 返回运行时类型,不能重写
public final native Class> getClass();
// ===== 2. hashCode() =====
// 默认返回对象内存地址转换的整数
public native int hashCode();
// ===== 3. equals() =====
// 默认比较引用地址
public boolean equals(Object obj) {
return (this == obj);
}
// ===== 4. clone() =====
// 创建对象副本,需要实现 Cloneable 接口
protected native Object clone() throws CloneNotSupportedException;
// ===== 5. toString() =====
// 默认返回:类名@十六进制哈希码
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
// ===== 6-10. 线程相关方法 =====
public final native void notify();
public final native void notifyAll();
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException { ... }
public final void wait() throws InterruptedException { ... }
// ===== 11. finalize() =====
// 已废弃,Java 9 标记 @Deprecated
@Deprecated(since="9")
protected void finalize() throws Throwable { }
}
```
## **二、== 运算符详解**
### **2.1 == 的行为规则**
```mermaid
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#e3f2fd",
"primaryTextColor": "#0d47a1",
"primaryBorderColor": "#2196f3",
"lineColor": "#546e7a",
"fontSize": "14px",
"fontFamily": "arial"
},
"flowchart": {
"curve": "basis",
"htmlLabels": true
}
}}%%
flowchart TB
%% 样式定义
classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:2px,color:#0d47a1;
classDef typeNode fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
classDef logicNode fill:#f1f8e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
classDef codeNode fill:#f5f5f5,stroke:#9e9e9e,stroke-width:1px,color:#333,font-family:monospace;
%% 核心结构
Start(["== 运算符的行为"]) --> Root{"比较对象 a == b"}
%% 分支1:基本类型
Root --> Primitive["基本数据类型
(Primitive Types)"]
Primitive --> PrimLogic["比较【值】是否相等"]
subgraph PrimExample ["基本类型示例"]
direction TB
PCode["int a = 5;
int b = 5;"]
PRes{"a == b → true"}
PCode --- PRes
end
%% 分支2:引用类型
Root --> Reference["引用数据类型
(Reference Types)"]
Reference --> RefLogic["比较【内存地址】
是否指向同一对象"]
subgraph RefExample ["引用类型示例"]
direction TB
RCode["Object o1 = new Object();
Object o2 = new Object();"]
RRes1["o1 == o2 → false"]
RRes2["o1 == o1 → true"]
RCode --- RRes1
RCode --- RRes2
end
%% 连接示例
PrimLogic ==> PrimExample
RefLogic ==> RefExample
%% 类应用
class Start main;
class Root typeNode;
class Primitive,Reference typeNode;
class PrimLogic,RefLogic logicNode;
class PCode,RCode,PRes,RRes1,RRes2 codeNode;
```
### **2.2 基本类型的 == 比较**
```java
public class PrimitiveEqualsDemo {
public static void main(String[] args) {
// ===== 整数类型 =====
int a = 100;
int b = 100;
System.out.println("int: " + (a == b)); // true
long c = 100L;
System.out.println("int == long: " + (a == c)); // true(自动类型提升)
// ===== 浮点类型 =====
double d1 = 0.1 + 0.2;
double d2 = 0.3;
System.out.println("double: " + (d1 == d2)); // false!(精度问题)
System.out.println("d1 = " + d1); // 0.30000000000000004
// ===== 字符类型 =====
char ch1 = 'A';
char ch2 = 65;
System.out.println("char: " + (ch1 == ch2)); // true('A'的ASCII码是65)
// ===== 布尔类型 =====
boolean bool1 = true;
boolean bool2 = true;
System.out.println("boolean: " + (bool1 == bool2)); // true
}
}
```
### **2.3 引用类型的 == 比较**
```java
public class ReferenceEqualsDemo {
public static void main(String[] args) {
// ===== 普通对象 =====
Person p1 = new Person("张三", 20);
Person p2 = new Person("张三", 20);
Person p3 = p1;
System.out.println("p1 == p2: " + (p1 == p2)); // false(不同对象)
System.out.println("p1 == p3: " + (p1 == p3)); // true(同一对象)
// ===== String 特殊情况 =====
String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");
System.out.println("s1 == s2: " + (s1 == s2)); // true(字符串常量池)
System.out.println("s1 == s3: " + (s1 == s3)); // false(s3是新对象)
// ===== 包装类特殊情况 =====
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println("i1 == i2: " + (i1 == i2)); // true(缓存池 -128~127)
System.out.println("i3 == i4: " + (i3 == i4)); // false(超出缓存范围)
}
}
```
### **2.4 == 比较内存图解**
```mermaid
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#e3f2fd",
"primaryTextColor": "#0d47a1",
"primaryBorderColor": "#2196f3",
"lineColor": "#546e7a",
"fontSize": "14px",
"tertiaryColor": "#f5f5f5"
},
"flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%
flowchart TB
subgraph Primitive ["基本类型:比较栈中的值"]
direction LR
subgraph Stack1 ["栈内存"]
A1["a = 100"]
B1["b = 100"]
end
Compare1{"100 == 100"}
Result1(["true"])
A1 --> Compare1
B1 --> Compare1
Compare1 ==> Result1
end
subgraph Reference ["引用类型:比较栈中的地址"]
direction TB
subgraph MemoryLayout ["内存结构"]
direction LR
subgraph Stack2 ["栈内存 (存储地址)"]
P1["p1 = 0x100"]
P2["p2 = 0x200"]
P3["p3 = 0x100"]
end
subgraph Heap ["堆内存 (实际对象)"]
ObjA[("Person 对象 A
(0x100)")]
ObjB[("Person 对象 B
(0x200)")]
end
P1 ==> ObjA
P3 ==> ObjA
P2 ==> ObjB
end
subgraph Logic ["逻辑判断"]
direction LR
Cond1{"p1 == p2
(0x100 == 0x200)"} -.-> Res1(["false"])
Cond2{"p1 == p3
(0x100 == 0x100)"} ==> Res2(["true"])
end
end
%% 样式定义
classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
classDef term fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
classDef storage fill:#f3e5f5,stroke:#9c27b0,stroke-width:1.5px,color:#4a148c;
class A1,B1,P1,P2,P3 main;
class Compare1,Cond1,Cond2 decision;
class Result1,Res1,Res2 term;
class ObjA,ObjB storage;
```
## **三、equals() 方法详解**
### **3.1 Object.equals() 默认实现**
```java
// Object 类中的默认实现
public boolean equals(Object obj) {
return (this == obj); // 默认就是比较引用!
}
```
```mermaid
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#e3f2fd",
"primaryTextColor": "#0d47a1",
"primaryBorderColor": "#2196f3",
"lineColor": "#546e7a",
"fontSize": "14px",
"tertiaryColor": "#f5f5f5"
},
"flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%
flowchart TB
%% 样式定义
classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
classDef code fill:#f5f5f5,stroke:#90a4ae,stroke-width:1px,color:#263238;
classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
classDef warning fill:#ffebee,stroke:#f44336,stroke-width:1px,color:#b71c1c;
classDef solution fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
subgraph DefaultBehavior ["Object.equals() 默认行为解析"]
direction TB
Start(["开始调用 p1.equals(p2)"]) --> Logic{"内部实现逻辑"}
Logic -- "默认实现" --> RawCode["return (this == obj);"]
subgraph InstanceComparison ["堆内存对象对比"]
direction LR
P1[("p1 (张三, 20)")]
P2[("p2 (张三, 20)")]
P1 -. "物理地址不同" .-> P2
end
RawCode --> Compare{地址是否一致?}
Compare -- "NO" --> ResultFalse(["结果: false"])
Problem["问题点
内容完全相同的对象
被判定为不相等"]
ResultFalse ==> Problem
end
subgraph SolutionPath ["改进方案"]
direction TB
Overriding[["重写 equals() 方法"]]
LogicChange["改为比较成员变量
(name, age)"]
ResultTrue(["预期结果: true"])
Overriding --> LogicChange --> ResultTrue
end
Problem ==> Overriding
%% 节点样式应用
class Start,ResultFalse main;
class RawCode,InstanceComparison code;
class Logic,Compare decision;
class Problem warning;
class Overriding,ResultTrue solution;
```
### **3.2 String.equals() 源码分析**
```java
/**
* String 类重写的 equals 方法(JDK 8)
*/
public boolean equals(Object anObject) {
// 1. 首先检查是否是同一个对象(快速路径)
if (this == anObject) {
return true;
}
// 2. 检查类型是否是 String
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
// 3. 比较长度
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
// 4. 逐字符比较
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
```
**流程**图:
```mermaid
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#e3f2fd",
"primaryTextColor": "#0d47a1",
"primaryBorderColor": "#2196f3",
"lineColor": "#546e7a",
"fontSize": "14px",
"tertiaryColor": "#f5f5f5"
},
"flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%
flowchart TD
%% 样式定义
classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
classDef term fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
classDef error fill:#ffebee,stroke:#f44336,stroke-width:1.5px,color:#b71c1c;
%% 节点定义
Start(["开始比较 String.equals(anObject)"])
CheckRef{"this == anObject
(引用地址相同?)"}
CheckType{"anObject instanceof String
(是否为字符串?)"}
CheckLength{"length 相同?"}
CheckChars{"逐字符循环比较
全部字符相同?"}
RetTrue(["return true"])
RetFalse(["return false"])
%% 流程连接
Start --> CheckRef
CheckRef == "是 (YES)" ==> RetTrue
CheckRef -- "否 (NO)" --> CheckType
CheckType -. "否 (NO)" .-> RetFalse
CheckType == "是 (YES)" ==> CheckLength
CheckLength -. "否 (NO)" .-> RetFalse
CheckLength == "是 (YES)" ==> CheckChars
CheckChars == "是 (YES)" ==> RetTrue
CheckChars -. "否 (NO)" .-> RetFalse
%% 应用样式
class Start,CheckLength,CheckChars main;
class CheckRef,CheckType decision;
class RetTrue term;
class RetFalse error;
%% 子图修饰
subgraph Logic ["String.equals() 内部逻辑路径"]
CheckRef
CheckType
CheckLength
CheckChars
end
```
### **3.3 正确重写 equals() 方法**
```java
public class Person {
private String name;
private int age;
private Address address;
// 构造方法、Getter/Setter 省略
/**
* 重写 equals 方法
*/
@Override
public boolean equals(Object obj) {
// 1. 检查是否是同一个对象的引用
if (this == obj) {
return true;
}
// 2. 检查 obj 是否为 null
if (obj == null) {
return false;
}
// 3. 检查是否是同一个类型
// 方式一:getClass()(严格类型匹配)
if (getClass() != obj.getClass()) {
return false;
}
// 方式二:instanceof(允许子类相等,但可能违反对称性)
// if (!(obj instanceof Person)) {
// return false;
// }
// 4. 强制类型转换
Person other = (Person) obj;
// 5. 比较关键属性
// 基本类型:用 ==
if (this.age != other.age) {
return false;
}
// 引用类型:用 equals(注意 null 处理)
if (this.name == null) {
if (other.name != null) {
return false;
}
} else if (!this.name.equals(other.name)) {
return false;
}
// 使用 Objects.equals() 更简洁
// return age == other.age &&
// Objects.equals(name, other.name) &&
// Objects.equals(address, other.address);
return true;
}
}
```
### **3.4 使用 Objects.equals() 简化**
```java
import java.util.Objects;
public class Person {
private String name;
private int age;
private Address address;
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
// 使用 Objects.equals() 自动处理 null
return age == person.age &&
Objects.equals(name, person.name) &&
Objects.equals(address, person.address);
}
}
```
**Objects.equals()** 源码:
```java
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
```
### **3.5 equals() 的五个规范**
```mermaid
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#e3f2fd",
"primaryTextColor": "#0d47a1",
"primaryBorderColor": "#2196f3",
"lineColor": "#546e7a",
"fontSize": "14px",
"tertiaryColor": "#f5f5f5"
},
"flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%
flowchart TB
%% 样式定义
classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
classDef highlight fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
classDef ruleBox fill:#ffffff,stroke:#cfd8dc,stroke-width:1px,color:#37474f;
Title(["Java equals() 方法遵循的五大规范"])
subgraph CoreRules ["核心等价关系规范"]
direction TB
Rule1["1. 自反性 (Reflexive)"]
Desc1["x.equals(x) 恒为 true
(对象必须等于自身)"]
Rule2["2. 对称性 (Symmetric)"]
Desc2["若 x.equals(y) 为 true
则 y.equals(x) 必为 true"]
Rule3["3. 传递性 (Transitive)"]
Desc3["若 x.equals(y) 且 y.equals(z) 为 true
则 x.equals(z) 必为 true"]
Rule1 --- Desc1
Rule2 --- Desc2
Rule3 --- Desc3
end
subgraph StabilityRules ["稳定性与健壮性规范"]
direction TB
Rule4["4. 一致性 (Consistent)"]
Desc4["只要对象状态未变
多次调用结果必须一致"]
Rule5["5. 非空性 (Non-nullity)"]
Desc5["x.equals(null) 必须返回 false
(任何对象不等于 null)"]
Rule4 --- Desc4
Rule5 --- Desc5
end
%% 连接标题与子图
Title ==> CoreRules
Title ==> StabilityRules
%% 应用样式
class Title highlight
class Rule1,Rule2,Rule3,Rule4,Rule5 main
class Desc1,Desc2,Desc3,Desc4,Desc5 ruleBox
```
## **四、== 与 equals() 对比**
### **4.1 核心区别**
| **对比项** | **==** | **equals()** |
| --- | --- | --- |
| **本质** | 运算符 | 方法 |
| **基本类型** | 比较值 | 不适用(基本类型没有方法) |
| **引用类型** | 比较地址 | 默认比较地址,可重写比较内容 |
| **能否重写** | 不能 | 可以 |
| **null 处理** | 可以用于 null | null.equals() 会 NPE |
---
### **4.2 对比图解**
```mermaid
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#e3f2fd",
"primaryTextColor": "#0d47a1",
"primaryBorderColor": "#2196f3",
"lineColor": "#546e7a",
"fontSize": "14px",
"tertiaryColor": "#f5f5f5"
},
"flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%
flowchart TB
%% 样式定义
classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
classDef term fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
classDef storage fill:#f3e5f5,stroke:#9c27b0,stroke-width:1.5px,color:#4a148c;
%% 场景初始化
Start(["场景:比较两个 Person 对象
p1 = new Person('张三', 20)
p2 = new Person('张三', 20)"])
subgraph DoubleEqual ["== 运算符比较"]
direction TB
Op1["p1 == p2"]
Comp1["比较堆内存引用地址
(0x100 == 0x200 ?)"]
Res1(["false
(不是同一个对象)"])
Op1 --> Comp1
Comp1 --> Res1
end
subgraph EqualsMethod ["equals() 方法比较"]
direction TB
Op2["p1.equals(p2)"]
CheckOverride{"是否重写了
equals 方法?"}
NoOverride["未重写 (调用 Object 类)"]
HasOverride["已重写 (业务逻辑)"]
Comp2["比较引用地址
(逻辑同 ==)"]
Comp3["比较对象内容
(name.equals && age ==)"]
Res2(["false"])
Res3(["true
(内容逻辑相同)"])
Op2 --> CheckOverride
CheckOverride -- "No" --> NoOverride
CheckOverride -- "Yes" --> HasOverride
NoOverride --> Comp2
HasOverride --> Comp3
Comp2 --> Res2
Comp3 ==> Res3
end
%% 顶层连接
Start ~~~ DoubleEqual
Start ~~~ EqualsMethod
%% 应用样式
class Start storage;
class Op1,Op2 main;
class CheckOverride decision;
class Res1,Res2,Res3 term;
```
### **4.3 常见陷阱与最佳实践**
```java
public class EqualsTrapsDemo {
public static void main(String[] args) {
// ===== 陷阱 1:用 == 比较字符串内容 =====
String input = new String("admin");
// ❌ 错误写法
if (input == "admin") {
System.out.println("永远不会执行");
}
// ✅ 正确写法
if (input.equals("admin")) {
System.out.println("正确判断");
}
// ✅ 更好的写法:常量在前,避免 NPE
if ("admin".equals(input)) {
System.out.println("避免空指针");
}
// ===== 陷阱 2:用 == 比较包装类 =====
Integer a = 128;
Integer b = 128;
// ❌ 可能出错(超出缓存范围)
if (a == b) {
System.out.println("可能不会执行");
}
// ✅ 正确写法
if (a.equals(b)) {
System.out.println("正确判断");
}
// ✅ 或者拆箱比较
if (a.intValue() == b.intValue()) {
System.out.println("正确判断");
}
// ===== 陷阱 3:null 调用 equals =====
String str = null;
// ❌ 会抛出 NullPointerException
// if (str.equals("test")) { }
// ✅ 使用 Objects.equals()
if (Objects.equals(str, "test")) {
System.out.println("安全比较");
}
}
}
```
## **五、hashCode() 方法详解**
### **5.1 hashCode() 的作用**
```mermaid
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#e3f2fd",
"primaryTextColor": "#0d47a1",
"primaryBorderColor": "#2196f3",
"lineColor": "#546e7a",
"fontSize": "14px",
"tertiaryColor": "#f5f5f5"
},
"flowchart": { "curve": "basis", "htmlLabels": true }
}}%%
flowchart TB
%% 核心概念定义
Start(["hashCode() 的作用"])
Definition["返回对象的哈希码 (int)
主要用于哈希表快速定位"]
subgraph Comparison ["查找效率对比"]
direction LR
subgraph Traditional ["没有 hashCode"]
T1["逐个比较 equals()"]
T2["时间复杂度: O(n)"]
T1 --> T2
end
subgraph Optimized ["有了 hashCode"]
O1["1. 计算 hashCode 定位桶位置"]
O2["2. 桶内进行 equals() 比较"]
O3["时间复杂度: 接近 O(1)"]
O1 ==> O2 ==> O3
end
end
%% 连接逻辑
Start --> Definition
Definition --> Comparison
%% 样式定义
classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
classDef term fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
classDef storage fill:#f3e5f5,stroke:#9c27b0,stroke-width:1.5px,color:#4a148c;
%% 应用样式
class Start,Definition main;
class O1,O2,O3 term;
class T1,T2 decision;
```
### **5.2 HashMap/HashSet 工作原理**
```mermaid
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#e3f2fd",
"primaryTextColor": "#0d47a1",
"primaryBorderColor": "#2196f3",
"lineColor": "#546e7a",
"fontSize": "14px",
"tertiaryColor": "#f5f5f5"
},
"flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%
flowchart TD
%% 样式定义
classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
classDef term fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
classDef storage fill:#f3e5f5,stroke:#9c27b0,stroke-width:1.5px,color:#4a148c;
%% 流程节点
Start(["开始: set.add(obj)"]) --> CalcHash["Step 1: 计算 hashCode
int hash = obj.hashCode()"]
CalcHash --> CalcIndex["Step 2: 计算桶位置
int index = hash % 数组长度"]
subgraph BucketProcess ["哈希表寻址与冲突检查"]
CalcIndex --> CheckEmpty{"Step 3: 检查桶是否为空"}
CheckEmpty -- "是 (null)" --> DirectInsert["直接插入到该索引位置"]
CheckEmpty -- "否 (已有数据)" --> TraverseList["Step 4: 遍历链表/红黑树
调用 equals() 逐一比较"]
TraverseList --> EqualCheck{"是否存在相同元素?"}
EqualCheck -- "找到相同
(equals == true)" --> Discard["放弃添加 (保证唯一性)"]
EqualCheck -- "未找到相同
(equals == false)" --> ListInsert["添加到链表末尾或红黑树"]
end
%% 结束节点
DirectInsert ==> Success(["添加成功"])
ListInsert ==> Success
Discard -.-> Fail(["添加失败 (已存在)"])
%% 指派样式
class Start,CalcHash,CalcIndex,TraverseList main;
class CheckEmpty,EqualCheck decision;
class Success,ListInsert,DirectInsert term;
class Discard,Fail storage;
```
### **5.3 哈希表结构图**
```mermaid
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#e3f2fd",
"primaryTextColor": "#0d47a1",
"primaryBorderColor": "#2196f3",
"lineColor": "#546e7a",
"fontSize": "14px",
"tertiaryColor": "#f5f5f5"
},
"flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%
flowchart TB
subgraph Container ["HashMap 内部数据结构 (Java 8+)"]
direction TB
subgraph ArrayLayer ["Node<K,V>[] table (数组/桶)"]
direction LR
B0["Index 0"]
B1["Index 1"]
B2["Index 2"]
B3["Index 3"]
B4["Index 4"]
B5["Index 5"]
B6["Index 6"]
B7["Index 7"]
end
%% 链表结构 1
B0 --> NodeA["Node A:1"]
NodeA --> NodeB["Node B:2"]
%% 空位
B1 --> Null1(["null"])
%% 链表结构 2
B3 --> NodeC["Node C:3"]
NodeC --> NodeD["Node D:4"]
%% 链表转红黑树示意
B5 --> NodeE["Node E:5"]
NodeE --> NodeF["Node F:6"]
NodeF --> NodeG["Node G:7"]
NodeG -.-> Tree{{"红黑树节点
TreeNode"}}
subgraph Logic ["转换逻辑说明"]
Rule["当链表长度 > 8 且数组长度 > 64 时"]
end
end
%% 样式定义
classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
classDef bucket fill:#f3e5f5,stroke:#9c27b0,stroke-width:1.5px,color:#4a148c;
classDef node fill:#ffffff,stroke:#546e7a,stroke-width:1px;
classDef special fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
classDef tip fill:#e8f5e9,stroke:#4caf50,stroke-dasharray: 5 5;
class B0,B1,B2,B3,B4,B5,B6,B7 bucket;
class NodeA,NodeB,NodeC,NodeD,NodeE,NodeF,NodeG node;
class Tree special;
class Rule tip;
class Container main;
```
### **5.4 Object.hashCode() 默认实现**
```
// Object 类中的默认实现(native 方法)
public native int hashCode();
```
```mermaid
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#e3f2fd",
"primaryTextColor": "#0d47a1",
"primaryBorderColor": "#2196f3",
"lineColor": "#546e7a",
"fontSize": "14px",
"tertiaryColor": "#f5f5f5"
},
"flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%
flowchart TB
%% 样式定义
classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
classDef code fill:#f5f5f5,stroke:#90a4ae,stroke-width:1px,color:#263238,font-family:monospace;
classDef warning fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
classDef highlight fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
subgraph Header ["Object.hashCode() 默认行为解析"]
direction TB
Title["默认实现:基于对象内部标识(Native)计算
通常与内存地址相关,但由 JVM 具体实现决定"]
end
subgraph Characteristics ["核心特点"]
direction LR
C1["一致性
同一对象生命周期内
hashCode 保持不变"]
C2["区分性
不同对象通常拥有
不同的 hashCode"]
C3["冲突性
不同对象可能产生
相同 hash (概率极低)"]
end
subgraph Example ["代码实例与内存表现"]
direction TB
CodeBlock["Person p1 = new Person('张三', 20);
Person p2 = new Person('张三', 20);"]
subgraph Memory ["JVM 堆内存状态"]
direction LR
Obj1["对象 p1
(内容: 张三, 20)
Hash: 123"]
Obj2["对象 p2
(内容: 张三, 20)
Hash: 456"]
end
end
subgraph Consequence ["默认行为的影响"]
Impact{"内容相同
但 Hash 不同"}
Result["HashSet / HashMap 失效
p1 与 p2 会被视为两个独立元素
存入集合,导致逻辑重复"]
end
%% 逻辑连接
Title --> Characteristics
Characteristics --> CodeBlock
CodeBlock --> Obj1
CodeBlock --> Obj2
Obj1 -.-> Impact
Obj2 -.-> Impact
Impact ==> Result
%% 类应用
class Title,C1,C2,C3 main;
class CodeBlock code;
class Obj1,Obj2 highlight;
class Impact,Result warning;
```
## **六、equals 和 hashCode 的关系**
### **6.1 核心规范**
```mermaid
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#e3f2fd",
"primaryTextColor": "#0d47a1",
"primaryBorderColor": "#2196f3",
"lineColor": "#546e7a",
"fontSize": "14px",
"tertiaryColor": "#f5f5f5"
},
"flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%
flowchart TB
%% 样式定义
classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
classDef must fill:#e8f5e9,stroke:#4caf50,stroke-width:2px,color:#1b5e20;
classDef optional fill:#f5f5f5,stroke:#9e9e9e,stroke-dasharray: 5 5,color:#616161;
subgraph Contract ["Java equals & hashCode 契约关系"]
direction TB
%% 规则 1 路径
R1_Start{"a.equals(b) == true"}
R1_Result(["必须相等 (Consistency)"])
R1_Start == "强制要求" ==> R1_Result
%% 规则 2 路径
R2_Start{"a.hashCode() == b.hashCode()"}
R2_True["a.equals(b) == true"]
R2_False["a.equals(b) == false"]
R2_Start -. "可能" .-> R2_True
R2_Start -. "也可能 (哈希冲突)" .-> R2_False
%% 规则 3 路径
R3_Start{"a.equals(b) == false"}
R3_Diff(["推荐:提高散列性能"])
R3_Same(["允许:但产生冲突"])
R3_Start ==> R3_Diff
R3_Start -. "允许但性能下降" .-> R3_Same
end
%% 应用样式
class R1_Start,R2_Start,R3_Start decision
class R1_Result,R3_Diff must
class R2_True main
class R2_False,R3_Same optional
%% 补充说明
note1["规则 1:
如果两个对象相等,
它们的 HashCode 必须相同"]
note2["规则 2:
HashCode 相同不代表对象相等
(Bucket 碰撞)"]
note1 --- R1_Start
note2 --- R2_Start
```
### **6.2 图解规则**
```mermaid
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#e3f2fd",
"primaryTextColor": "#0d47a1",
"primaryBorderColor": "#2196f3",
"lineColor": "#546e7a",
"fontSize": "14px",
"tertiaryColor": "#f5f5f5"
},
"flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%
flowchart TB
%% 样式定义
classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
classDef success fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
classDef error fill:#ffebee,stroke:#f44336,stroke-width:1.5px,color:#b71c1c;
%% 核心逻辑
Start(["开始判断对象关系"]) --> HashCheck{"hashCode 是否相等?"}
%% hashCode 相等分支
subgraph HashEqualGroup ["hashCode 相等 (存在潜在关联)"]
HashCheck -- "相等" --> EqualsCheck{"equals 是否相等?"}
EqualsCheck -- "相等" --> Case1["✅ 合法:对象完全相同"]
EqualsCheck -- "不等" --> Case2["✅ 合法:哈希冲突 (Collision)"]
end
%% hashCode 不相等分支
subgraph HashNotEqualGroup ["hashCode 不相等"]
HashCheck -- "不相等" --> Case3["✅ 合法:equals 必定不等"]
end
%% 违规约束
Constraint{"违反原则的情况"} -.-> Illegal["❌ 非法:equals 相等但 hashCode 不等"]
%% 节点样式应用
class Start main;
class HashCheck,EqualsCheck decision;
class Case1,Case2,Case3 success;
class Illegal error;
%% 补充说明
note1["注:Java 规范要求若 equals 相等,则 hashCode 必须相等"]
Case1 ~~~ note1
style note1 fill:#fafafa,stroke:#ccc,stroke-dasharray: 5 5,color:#666;
```
### **6.3 为什么重写 equals 必须重写 hashCode?**
```java
/**
* 只重写 equals,不重写 hashCode 的问题演示
*/
public class BrokenPerson {
private String name;
private int age;
public BrokenPerson(String name, int age) {
this.name = name;
this.age = age;
}
// 只重写了 equals
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
BrokenPerson other = (BrokenPerson) obj;
return age == other.age && Objects.equals(name, other.name);
}
// ❌ 没有重写 hashCode!
}
public class BrokenDemo {
public static void main(String[] args) {
BrokenPerson p1 = new BrokenPerson("张三", 20);
BrokenPerson p2 = new BrokenPerson("张三", 20);
// equals 返回 true
System.out.println("p1.equals(p2): " + p1.equals(p2)); // true
// 但 hashCode 不同!
System.out.println("p1.hashCode(): " + p1.hashCode()); // 例如:366712642
System.out.println("p2.hashCode(): " + p2.hashCode()); // 例如:1829164700
// ===== 在 HashSet 中出问题 =====
Set set = new HashSet<>();
set.add(p1);
set.add(p2);
System.out.println("set.size(): " + set.size()); // 2 ❌ 应该是 1
// ===== 查找失败 =====
BrokenPerson p3 = new BrokenPerson("张三", 20);
System.out.println("set.contains(p3): " + set.contains(p3)); // false ❌
}
}
```
问题分析:
```mermaid
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#e3f2fd",
"primaryTextColor": "#0d47a1",
"primaryBorderColor": "#2196f3",
"lineColor": "#546e7a",
"fontSize": "14px",
"tertiaryColor": "#f5f5f5"
},
"flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%
flowchart TB
subgraph Analysis ["问题根源分析:未重写 hashCode 导致重复"]
direction TB
subgraph Objects ["对象状态 (p1.equals(p2) == true)"]
P1["对象 p1
hashCode: 366712642"]
P2["对象 p2
hashCode: 1829164700"]
end
subgraph Process ["HashSet.add() 内部逻辑"]
direction LR
Calc1["计算 p1 桶位
366712642 % 16 = 2"]
Calc2["计算 p2 桶位
1829164700 % 16 = 12"]
end
subgraph Storage ["HashMap 桶数组 (Buckets)"]
direction LR
B2[("桶 [2]")]
B12[("桶 [12]")]
Other["其他桶..."]
end
subgraph Result ["最终结果"]
Fail["逻辑冲突
p1 与 p2 位于不同桶
无法触发 equals() 检查"]
end
end
%% 连线关系
P1 ==> Calc1
P2 ==> Calc2
Calc1 ==> B2
Calc2 ==> B12
B2 -.-> Result
B12 -.-> Result
%% 样式定义
classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
classDef storage fill:#f3e5f5,stroke:#9c27b0,stroke-width:1.5px,color:#4a148c;
classDef alert fill:#ffebee,stroke:#f44336,stroke-width:2px,color:#b71c1c;
class P1,P2,Calc1,Calc2 main;
class B2,B12,Other storage;
class Result,Fail alert;
```
### **6.4 正确实现**
```java
import java.util.Objects;
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// ✅ 正确重写 equals
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && Objects.equals(name, person.name);
}
// ✅ 正确重写 hashCode(使用相同的属性)
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
```
**验**证:
```java
public class CorrectDemo {
public static void main(String[] args) {
Person p1 = new Person("张三", 20);
Person p2 = new Person("张三", 20);
System.out.println("p1.equals(p2): " + p1.equals(p2)); // true
System.out.println("p1.hashCode(): " + p1.hashCode()); // 相同
System.out.println("p2.hashCode(): " + p2.hashCode()); // 相同
Set set = new HashSet<>();
set.add(p1);
set.add(p2);
System.out.println("set.size(): " + set.size()); // 1 ✅
System.out.println("set.contains(new Person(\"张三\", 20)): "
+ set.contains(new Person("张三", 20))); // true ✅
}
}
```
## **七、Objects.hash() 源码分析**
### **7.1 源码**
```java
/**
* java.util.Objects 类
*/
public final class Objects {
public static int hash(Object... values) {
return Arrays.hashCode(values);
}
}
/**
* java.util.Arrays 类
*/
public class Arrays {
public static int hashCode(Object[] a) {
if (a == null)
return 0;
int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());
return result;
}
}
```
### **7.2 为什么使用 31 作为乘数?**
```mermaid
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#e3f2fd",
"primaryTextColor": "#0d47a1",
"primaryBorderColor": "#2196f3",
"lineColor": "#546e7a",
"fontSize": "14px",
"tertiaryColor": "#f5f5f5"
},
"flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%
flowchart TB
%% 核心公式展示
Formula(["hash = 31 * hash + element.hashCode()"])
%% 三大核心理由
subgraph Reasons ["为什么选择 31?"]
direction TB
subgraph Math ["1. 数学特性 (奇素数)"]
direction TB
Prime["素数 (Prime)
减少哈希冲突的可能性"]
Odd["奇数 (Odd)
避免乘法溢出时低位丢失信息"]
end
subgraph Perf ["2. 性能优化 (JVM)"]
direction TB
Shift["位运算优化
31 * i == (i << 5) - i"]
Fast["执行效率高
移位和减法比传统乘法更快"]
end
subgraph Practice ["3. 工业实践 (经验值)"]
direction TB
Uniform["分布均匀
在大数据测试中冲突率极低"]
Standard["标准实现
JDK String 等类广泛采用"]
end
end
%% 连接关系
Formula ==> Reasons
Math ~~~ Perf
Perf ~~~ Practice
%% 样式定义
classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
classDef highlight fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
classDef subbox fill:#f9f9f9,stroke:#cfd8dc,stroke-dasharray: 5 5;
class Formula highlight;
class Prime,Odd,Shift,Fast,Uniform,Standard main;
class Math,Perf,Practice subbox;
```
### **7.3 手动实现 hashCode**
```java
public class Person {
private String name;
private int age;
private Address address;
// 方式一:使用 Objects.hash()(推荐)
@Override
public int hashCode() {
return Objects.hash(name, age, address);
}
// 方式二:手动实现
@Override
public int hashCode() {
int result = 17; // 非零初始值
result = 31 * result + (name == null ? 0 : name.hashCode());
result = 31 * result + age;
result = 31 * result + (address == null ? 0 : address.hashCode());
return result;
}
// 方式三:Java 7+ 使用 Objects 工具类
@Override
public int hashCode() {
return Objects.hash(name, age, address);
}
}
```
## **八、最佳实践**
### **8.1 使用 IDE 自动生成**
```java
/**
* IDEA 自动生成的 equals 和 hashCode
* 快捷键:Alt + Insert → equals() and hashCode()
*/
public class Person {
private String name;
private int age;
private Address address;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name) &&
Objects.equals(address, person.address);
}
@Override
public int hashCode() {
return Objects.hash(name, age, address);
}
}
```
### **8.2 使用 Lombok 注解**
```java
import lombok.EqualsAndHashCode;
import lombok.Data;
// 方式一:只生成 equals 和 hashCode
@EqualsAndHashCode
public class Person {
private String name;
private int age;
}
// 方式二:使用 @Data(包含 equals、hashCode、toString、getter、setter)
@Data
public class Person {
private String name;
private int age;
}
// 方式三:排除某些字段
@EqualsAndHashCode(exclude = {"id", "createTime"})
public class Person {
private Long id;
private String name;
private int age;
private LocalDateTime createTime;
}
// 方式四:只包含某些字段
@EqualsAndHashCode(of = {"name", "age"})
public class Person {
private Long id;
private String name;
private int age;
}
```
### **8.3 注意事项**
```mermaid
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#e3f2fd",
"primaryTextColor": "#0d47a1",
"primaryBorderColor": "#2196f3",
"lineColor": "#546e7a",
"fontSize": "14px",
"tertiaryColor": "#f5f5f5"
},
"flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%
flowchart TB
subgraph CorePrinciple ["核心原则:一致性与不变性"]
P1["1. 属性同步:equals 与 hashCode 必须使用完全相同的属性集合"]
P2["2. 不变性:参与计算的属性应尽量为不可变 (final)"]
end
subgraph Implementation ["实现细节与最佳实践"]
direction LR
subgraph Safety ["安全性保障"]
S1{"类型检查"}
S1 -- "推荐" --> S1_A["使用 getClass()
(保证对称性)"]
S1 -. "慎用" .-> S1_B["使用 instanceof
(继承场景易出错)"]
S2["Null 安全"]
S2 --> S2_A["使用 Objects.equals()"]
S2 --> S2_B["使用 Objects.hash()"]
end
end
subgraph RiskWarning ["风险警示:违规后果"]
R1[("HashSet/HashMap")]
R2["修改已存入对象的属性"]
R3["hashCode 发生改变"]
R4["无法检索到该对象 (内存泄漏风险)"]
R1 --> R2 --> R3 --> R4
end
%% 逻辑连接
CorePrinciple ==> Implementation
P2 -. "如果不遵守" .-> RiskWarning
%% 样式定义
classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
classDef term fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
classDef warning fill:#ffebee,stroke:#f44336,stroke-width:1.5px,color:#b71c1c;
class P1,P2,S2_A,S2_B main;
class S1 decision;
class S1_A,S1_B term;
class R1,R2,R3,R4 warning;
```
## **九、toString() 方法**
### **9.1 默认实现**
```java
// Object 类中的默认实现
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
```
```
Person p = new Person("张三", 20);
System.out.println(p.toString());
// 输出:com.example.Person@1b6d3586
```
### **9.2 重写 toString()**
```java
public class Person {
private String name;
private int age;
// 方式一:手动拼接
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
// 方式二:使用 String.format
@Override
public String toString() {
return String.format("Person{name='%s', age=%d}", name, age);
}
// 方式三:使用 StringBuilder
@Override
public String toString() {
return new StringBuilder()
.append("Person{")
.append("name='").append(name).append("'")
.append(", age=").append(age)
.append("}")
.toString();
}
// 方式四:使用 StringJoiner(Java 8+)
@Override
public String toString() {
return new StringJoiner(", ", Person.class.getSimpleName() + "[", "]")
.add("name='" + name + "'")
.add("age=" + age)
.toString();
}
}
```
## **十、核心总结**
### **10.1 速查表**
| **方法** | **默认行为** | **是否需要重写** | **重写目的** |
| --- | --- | --- | --- |
| `equals()` | 比较引用地址 | ✅ 通常需要 | 比较对象内容 |
| `hashCode()` | 基于内存地址 | ✅ 通常需要 | 配合哈希表使用 |
| `toString()` | 类名@哈希码 | ✅ 建议重写 | 输出有意义的信息 |
| `clone()` | 浅拷贝 | ⚠️ 按需重写 | 实现深拷贝 |
| `getClass()` | 返回 Class 对象 | ❌ 不能重写 | final 方法 |
| `wait/notify` | 线程等待/唤醒 | ❌ 不能重写 | final 方法 |
| `finalize()` | 空实现 | ❌ 已废弃 | 不建议使用 |
---
### **10.2 记忆口诀**
```mermaid
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#e3f2fd",
"primaryTextColor": "#0d47a1",
"primaryBorderColor": "#2196f3",
"lineColor": "#546e7a",
"fontSize": "14px",
"tertiaryColor": "#f5f5f5"
},
"flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%
flowchart TB
%% 核心标题
Title(["Java 对象比较与哈希口诀指南"])
subgraph Comparison ["== 与 equals:核心区别"]
C1["基本比值,引用比址"]
C2["equals 默认同 ==
重写才比内容"]
C1 --- C2
end
subgraph HashContract ["equals 与 hashCode:契约关系"]
H1["equals 相等 ==> hashCode 必等"]
H2["hashCode 相等 -.-> equals 不一定"]
H3["重写一个,必须重写另一个"]
H4["使用相同的属性来计算"]
H1 --- H2
H2 --- H3
H3 --- H4
end
subgraph BestPractice ["实践口诀:避坑与提效"]
P1["比较字符串/包装类用 equals"]
P2["'常量'.equals(变量)
常量放前面,空指针不来"]
P3["IDE 一键生成
Lombok @Data 更简洁"]
P1 --- P2
P2 --- P3
end
%% 逻辑连接
Title ==> Comparison
Comparison ==> HashContract
HashContract ==> BestPractice
%% 样式定义
classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
classDef term fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
class Title main;
class C1,C2 decision;
class H1,H2,H3,H4 term;
class P1,P2,P3 main;
```
### **10.3 核心要点图**
```mermaid
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#e3f2fd",
"primaryTextColor": "#0d47a1",
"primaryBorderColor": "#2196f3",
"lineColor": "#546e7a",
"fontSize": "14px",
"tertiaryColor": "#f5f5f5"
},
"flowchart": { "curve": "basis", "htmlLabels": true, "useMaxWidth": true }
}}%%
flowchart TB
%% 核心样式定义
classDef main fill:#e3f2fd,stroke:#2196f3,stroke-width:1.5px,color:#0d47a1;
classDef decision fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px,color:#e65100;
classDef term fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px,color:#1b5e20;
classDef storage fill:#f3e5f5,stroke:#9c27b0,stroke-width:1.5px,color:#4a148c;
Root(["Java Object 核心机制总结"])
subgraph Hierarchy ["1. 根类地位"]
RootNode["Object 类"]
Methods["拥有 11 个核心方法"]
Focus["核心重写:equals / hashCode / toString"]
RootNode --> Methods
Methods --> Focus
end
subgraph Comparison ["2. == 与 equals 区别"]
Compare{"比较方式"}
OpEqual["== 运算符"]
MethodEqual["equals() 方法"]
Compare --> OpEqual
Compare --> MethodEqual
OpEqual -- "基本类型" --> V1["比较具体数值"]
OpEqual -- "引用类型" --> V2["比较内存地址"]
MethodEqual -- "默认" --> V2
MethodEqual -- "重写后" --> V3["比较对象内容"]
end
subgraph HashMechanism ["3. hashCode 与哈希表"]
HashRole["快速定位 (HashMap/HashSet)"]
Logic{"查找逻辑"}
Step1["1. 比较 hashCode"]
Step2["2. 调用 equals()"]
HashRole --> Logic
Logic --> Step1
Step1 -- "哈希冲突" --> Step2
end
subgraph Rules ["4. 重写规范 & 约束"]
Must["重写 equals 必须重写 hashCode"]
Property["两者必须基于相同属性计算"]
subgraph Contract ["五大特性"]
C1["自反性 / 对称性"]
C2["传递性 / 一致性"]
C3["非空性 (Not-null)"]
end
Must --> Property
Property --> Contract
end
subgraph BestPractice ["5. 最佳实践"]
ObjectsUtil["使用 Objects.equals() / hash()"]
AutoTool["IDE 自动生成 / Lombok (@Data)"]
SafeCheck["'常量'.equals(变量) 规避 NPE"]
end
%% 逻辑连接
Root ==> Hierarchy
Hierarchy ==> Comparison
Comparison ==> HashMechanism
HashMechanism ==> Rules
Rules ==> BestPractice
%% 应用样式
class RootNode,Root main;
class Compare,Logic decision;
class Root term;
class ObjectsUtil,AutoTool,SafeCheck storage;
```